package fina2.dcs.security;

import java.io.Serializable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.util.List;

import javax.annotation.Resource;
import javax.ejb.EJB;
import javax.ejb.Remove;
import javax.ejb.SessionContext;
import javax.ejb.Stateless;
import javax.ejb.TransactionManagement;
import javax.ejb.TransactionManagementType;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.NonUniqueResultException;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.apache.log4j.Logger;
import org.jboss.annotation.ejb.LocalBinding;
import org.jboss.seam.Component;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.AutoCreate;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;

import fina2.dcs.context.ContextHelper;
import fina2.dcs.fis.Bank;
import fina2.dcs.fis.BankType;
import fina2.dcs.i18n.LanguageSession;
import fina2.dcs.system.SysString;
import fina2.dcs.system.SysStringLocal;
import fina2.dcs.system.SysStringPk;
import fina2.dcs.system.SysUserBank;
import fina2.dcs.upload.UploadedFileLocal;

@Stateless
@Name("userSession")
@Scope(ScopeType.SESSION)
@LocalBinding(jndiBinding = "UserSession/local")
@AutoCreate
@TransactionManagement(TransactionManagementType.BEAN)
public class UserSession implements UserLocal, Serializable {

	private Logger log = Logger.getLogger(getClass());

	@PersistenceContext
	private EntityManager em;

	@Resource
	private SessionContext sc;

	@EJB
	private SysStringLocal sysLocal;
	@EJB
	private UserPermissionLocal userPermLocal;

	private List<String> userPerms = null;

	public boolean currentUserSa() {
		return getCurrentUser().getLogin().trim().equals("sa");
	}

	@Remove
	public void remove() {

	}

	public User findByLogin(String username) {

		User u = null;
		log.debug("Searching user " + username);
		try {
			Query q = em.createQuery("SELECT u FROM " + User.class.getName() + " u WHERE LOWER(RTRIM(u.login))=?");
			q.setParameter(1, username.toLowerCase());
			u = (User) q.getSingleResult();
			if (u != null) {
				SysString title=sysLocal.findByPk(new SysStringPk(u.getTitleStrid(), LanguageSession.langId));
				u.setTitle(title!=null?title.getValue():"");
				SysString name=sysLocal.findByPk(new SysStringPk(u.getNameStrid(), LanguageSession.langId));
				u.setName(name!=null?name.getValue():"");
				u.setUserPerms(userPermLocal.loadPermissionsByUserId(u.getId()));
			}

		} catch (NoResultException ex) {
			log.info("Could not find user " + username);
		} catch (NonUniqueResultException ex) {
			log.error(ex.getMessage(), ex);
		} catch (Exception ex) {
			log.error(ex.getMessage(), ex);
		}
		return u;
	}

	public User findByLoginPassword(String username, String pwdHash) {
		User u = null;
		log.info("Searching user " + username);
		try {
			Query q = em.createQuery("SELECT u FROM " + User.class.getName() + " u WHERE LOWER(RTRIM(u.login))=? AND RTRIM(u.password)=?");
			q.setParameter(1, username.toLowerCase());
			q.setParameter(2, pwdHash);
			u = (User) q.getSingleResult();

			u.setUserPerms(userPermLocal.loadPermissionsByUserId(u.getId()));
		} catch (NoResultException ex) {
			log.info("Could not find user with such login/password");
		} catch (NonUniqueResultException ex) {
			log.error(ex.getMessage(), ex);
		} catch (Exception ex) {
			log.error(ex.getMessage(), ex);
		}
		return u;
	}

	public User getCurrentUser() {
		User currentUser = null;
		try {

			String username = sc.getCallerPrincipal().getName();
			currentUser = findByLogin(username);

		} catch (Exception ex) {
			log.error(ex.getMessage(), ex);
		}
		return currentUser;
	}

	public String hashPassword(String password) {
		String hash = null;
		try {
			MessageDigest md = MessageDigest.getInstance("SHA-1");
			md.update(password.getBytes());
			BigInteger bi = new BigInteger(1, md.digest());
			hash = bi.toString(16);
		} catch (Exception ex) {
			log.error(ex.getMessage(), ex);
		}
		return hash.trim();
	}

	public boolean hasPermission(String permKey) {

		boolean hasPerm = false;

		try {
			User cu = getCurrentUser();
			if (cu.getLogin().trim().toLowerCase().equals("sa"))
				hasPerm = true;
			else {
				userPerms = cu.getUserPerms();
				if (userPerms != null)
					hasPerm = cu.getUserPerms().contains(permKey.trim());
			}
		} catch (Exception ex) {
			log.error(ex.getMessage(), ex);
		}
		return hasPerm;
	}

	public boolean hasPermission(BigDecimal userId, String permKey) {
		boolean hasPerm = false;
		try {
			userPerms = userPermLocal.loadPermissionsByUserId(userId);
			hasPerm = userPerms.contains(permKey);
		} catch (Exception ex) {
			log.error(ex.getMessage(), ex);
		}
		return hasPerm;
	}

	public String logout() {
		UploadedFileLocal ufl = (UploadedFileLocal) Component.getInstance("uploadFileSession");
		ufl.clear();
		org.jboss.seam.web.Session.instance().invalidate();

		ContextHelper.clearAll();
		return "logout";
	}

	public List<User> getAllUsers() {
		List<User> allUsers = em.createQuery("FROM " + User.class.getName() + " u ORDER BY u.login ASC").getResultList();
		return allUsers;
	}

	public List<String> getAllLogins() {
		return em.createQuery("SELECT u.login FROM " + User.class.getName() + " u ORDER BY u.login ASC").getResultList();

	}

	public void updateCurrentUser() {
		log.info("Updating user settings...");
		try {
			User currentUser = getCurrentUser();
			SysString ss = new SysString();
			SysStringPk pk = new SysStringPk(currentUser.getNameStrid(), LanguageSession.langId);
			ss.setValue(currentUser.getName());
			ss.setPk(pk);

			sysLocal.updateString(ss);

			SysString ssTitle = new SysString();
			SysStringPk ssTitlePk = new SysStringPk(currentUser.getTitleStrid(), LanguageSession.langId);
			ssTitle.setValue(currentUser.getTitle());
			ssTitle.setPk(ssTitlePk);

			sysLocal.updateString(ssTitle);

			if (currentUser.getPassword().trim().equals("*$*$*$*$")) {
				Query q = em.createQuery("UPDATE " + User.class.getName() + " u SET u.phone=:phone , u.email=:email WHERE u.id=:id");
				q.setParameter("id", currentUser.getId());
				q.setParameter("phone", currentUser.getPhone());
				q.setParameter("email", currentUser.getEmail());
				q.executeUpdate();

			} else {
				currentUser.setPassword(hashPassword(currentUser.getPassword()));
				em.merge(currentUser);
			}
			// currentUser.setPassword("*$*$*$*$");
			// currentUser.setConfirm("*$*$*$*$");
			log.info("User settings updated...");
		} catch (Exception ex) {
			log.error(ex.getMessage(), ex);
		}
	}

	public BigDecimal getUserId(String username) {
		BigDecimal id = null;
		try {
			Query q = em.createQuery("SELECT u.id FROM " + User.class.getName() + " u WHERE RTRIM(u.login)=:login");
			q.setParameter("login", username.trim());
			id = (BigDecimal) q.getSingleResult();
		} catch (Exception ex) {
			log.error(ex.getMessage(), ex);
		}
		return id;
	}

	public String findById(BigDecimal userId) {
		User u = em.find(User.class, userId);
		String username = null;
		if (u != null)
			username = u.getLogin().trim();
		return username;
	}

	public List<String> getUserPermissions(BigDecimal userId) {
		return userPermLocal.loadPermissionsByUserId(userId);
	}

	public List<String> loadUserFiTypes(BigDecimal userId) {
		Query q = em.createQuery("SELECT bt.code FROM " + BankType.class.getName() + " bt WHERE bt.id IN(SELECT b.typeId FROM " + Bank.class.getName() + " b WHERE b.id IN(SELECT sub.userBankPk.bankId FROM " + SysUserBank.class.getName() + " sub WHERE sub.userBankPk.userId=:userId))");
		q.setParameter("userId", userId);
		List<String> fiTypeCodes = q.getResultList();
		return fiTypeCodes;
	}

	public User findByEmail(String email) {
		User u = null;
		try {
			List<User> users = em.createQuery("FROM " + User.class.getName() + " u WHERE rtrim(u.email)=:email").setParameter("email", email).getResultList();
			if ((users != null) && (users.size()!=0))
				u = users.get(0);
		} catch (Exception ex) {
			log.error("Error during searching user with email : " + email);
			log.error(ex.getMessage(), ex);
		}
		return u;
	}
}
